﻿using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using SimpleOrmCFPC.Core.Query.Requests;
using SimpleOrmCFPC.Core.Query.Where;
using SimpleOrmCFPC.Core.Query.expressions.ExprssionCompilers.Helpers;
using SimpleOrmCFPC.Core.Query.expressions.RequestBuilder.WhereElmentsBuilder.Helpers;

namespace SimpleOrmCFPC.Core.Query.expressions.ExprssionCompilers
{
    internal class FromMethodCallCompileResultFactory : IWhereElementBuilder
    {
        private AnyWhereElementFromExpressionComputer whereElementBuilder;

        public FromMethodCallCompileResultFactory(AnyWhereElementFromExpressionComputer whereElementBuilder)
        {
            this.whereElementBuilder = whereElementBuilder;
        }

        public IWhereElement Compute(Expression expression)
        {
            var call = (MethodCallExpression) expression;
            IWhereElement[] args = ComputeArgs(call);

            if (IsLikeMethod(call, args))
            {
                return new LikeWhereElement(args[0], args[1]);
            }
            if (IsStartsWith(call, args))
            {
                return ComputeLike(call, t => t + "%");
            }
            if (IsEndsWith(call, args))
            {
                return ComputeLike(call, t => "%" + t);
            }
            if (IsContains(call, args))
            {
                return ComputeLike(call, t => "%" + t + "%");
            }
            return new MethodCallWhereElement(args, call.Method.Name);
        }

        private bool IsEndsWith(MethodCallExpression call, IWhereElement[] args)
        {
            return call.Method.Name == "EndsWith" && args.Length == 1;
        }

        private bool IsContains(MethodCallExpression call, IWhereElement[] args)
        {
            return call.Method.Name == "Contains" && args.Length == 1;
        }

        private IWhereElement ComputeLike(MethodCallExpression call, Func<object, string> text)
        {
            UnaryExpression objectMember = Expression.Convert(call.Arguments[0], typeof (object));

            Expression<Func<object>> getterLambda = Expression.Lambda<Func<object>>(objectMember);

            Func<object> getter = getterLambda.Compile();
            return new LikeWhereElement(whereElementBuilder.Compute(call.Object),
                                        new SimpleWhereElement("'" + text(getter()) + "'", new List<object>()));
        }

        public void FillAdditionalInformation(Expression expression, SQLRequest request)
        {
            var call = (MethodCallExpression) expression;

            foreach (Expression argument in call.Arguments)
            {
                whereElementBuilder.FillAdditionalInformation(argument, request);
            }
        }

        private static bool IsLikeMethod(MethodCallExpression call, IWhereElement[] args)
        {
            return call.Method.Name == "Like" && args.Length == 2;
        }

        private IWhereElement[] ComputeArgs(MethodCallExpression call)
        {
            var args = new IWhereElement[call.Arguments.Count];

            for (int i = 0; i < args.Length; i++)
            {
                args[i] = whereElementBuilder.Compute(call.Arguments[i]);
            }
            return args;
        }

        private bool IsStartsWith(MethodCallExpression call, IWhereElement[] args)
        {
            return call.Method.Name == "StartsWith" && args.Length == 1;
        }
    }
}